<!--
Copyright (c) 2024 Industrial Shields. All rights reserved

This file is part of node-red-librpiplc.

node-red-librpiplc is free software: you can redistribute
it and/or modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, either version
3 of the License, or (at your option) any later version.

node-red-librpiplc is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<script type="text/javascript">
	const rpiplcDigitalOutputsAnalog0 = [ "Q0.0", "Q0.1", "Q0.2", "Q0.3", "Q0.4", "Q0.5", "Q0.6", "Q0.7", "A0.5", "A0.6", "A0.7", ];
	const rpiplcDigitalOutputsAnalog1 = [ "Q1.0", "Q1.1", "Q1.2", "Q1.3", "Q1.4", "Q1.5", "Q1.6", "Q1.7", "A1.5", "A1.6", "A1.7", ];
	const rpiplcDigitalOutputsAnalog2 = [ "Q2.0", "Q2.1", "Q2.2", "Q2.3", "Q2.4", "Q2.5", "Q2.6", "Q2.7", "A2.5", "A2.6", "A2.7", ];
	const rpiplcDigitalOutputsRelay0 = [ "R0.1", "R0.2", "R0.3", "R0.4", "R0.5", "R0.6", "R0.7", "R0.8", "Q0.0", "Q0.1", "Q0.2", "A0.0", "A0.1", "A0.2", ];
	const rpiplcDigitalOutputsRelay1 = [ "R1.1", "R1.2", "R1.3", "R1.4", "R1.5", "R1.6", "R1.7", "R1.8", "Q1.0", "Q1.1", "Q1.2", "A1.0", "A1.1", "A1.2", ];
	const rpiplcDigitalOutputsRelay2 = [ "R2.1", "R2.2", "R2.3", "R2.4", "R2.5", "R2.6", "R2.7", "R2.8", "Q2.0", "Q2.1", "Q2.2", "A2.0", "A2.1", "A2.2", ];

	const rpiplcV6ExtraDigitalOutputs = ["PIN8", "P_RELAY", "OPTO_OUT_1", "OPTO_OUT_2", "INT31", "EXP1_RST", "EXP2_RST"];
	const rpiplcV6DigitalOutputPins = {
		"RPIPLC_19R": [...rpiplcV6ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0],
		"RPIPLC_21": [...rpiplcV6ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0],
		"RPIPLC_38AR": [...rpiplcV6ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsRelay1],
		"RPIPLC_38R": [...rpiplcV6ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0, ...rpiplcDigitalOutputsRelay1],
		"RPIPLC_42": [...rpiplcV6ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsAnalog1],
		"RPIPLC_50RRA": [...rpiplcV6ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsAnalog2],
		"RPIPLC_53ARR": [...rpiplcV6ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsRelay2],
		"RPIPLC_54ARA": [...rpiplcV6ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsAnalog2],
		"RPIPLC_57AAR": [...rpiplcV6ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsAnalog1, ...rpiplcDigitalOutputsRelay2],
		"RPIPLC_57R": [...rpiplcV6ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsRelay2],
		"RPIPLC_58": [...rpiplcV6ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsAnalog1, ...rpiplcDigitalOutputsAnalog2]
	};

	const rpiplcV4ExtraDigitalOutputs = ["PWM1", "PWM2", "PWM3", "EXP1_RST", "EXP2_RST"];
	const rpiplcV4DigitalOutputPins = {
		"RPIPLC_19R": [...rpiplcV4ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0],
		"RPIPLC_21": [...rpiplcV4ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0],
		"RPIPLC_38AR": [...rpiplcV4ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsRelay1],
		"RPIPLC_38R": [...rpiplcV4ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0, ...rpiplcDigitalOutputsRelay1],
		"RPIPLC_42": [...rpiplcV4ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsAnalog1],
		"RPIPLC_50RRA": [...rpiplcV4ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsAnalog2],
		"RPIPLC_53ARR": [...rpiplcV4ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsRelay2],
		"RPIPLC_54ARA": [...rpiplcV4ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsAnalog2],
		"RPIPLC_57AAR": [...rpiplcV4ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsAnalog1, ...rpiplcDigitalOutputsRelay2],
		"RPIPLC_57R": [...rpiplcV4ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsRelay2],
		"RPIPLC_58": [...rpiplcV4ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsAnalog1, ...rpiplcDigitalOutputsAnalog2]
	};

	const rpiplcV3ExtraDigitalOutputs = [];
	const rpiplcV3DigitalOutputPins = {
		"RPIPLC_19R": [...rpiplcV3ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0],
		"RPIPLC_21": [...rpiplcV3ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0],
		"RPIPLC_38AR": [...rpiplcV3ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsRelay1],
		"RPIPLC_38R": [...rpiplcV3ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0, ...rpiplcDigitalOutputsRelay1],
		"RPIPLC_42": [...rpiplcV3ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsAnalog1],
		"RPIPLC_50RRA": [...rpiplcV3ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsAnalog2],
		"RPIPLC_53ARR": [...rpiplcV3ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsRelay2],
		"RPIPLC_54ARA": [...rpiplcV3ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsAnalog2],
		"RPIPLC_57AAR": [...rpiplcV3ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsAnalog1, ...rpiplcDigitalOutputsRelay2],
		"RPIPLC_57R": [...rpiplcV3ExtraDigitalOutputs, ...rpiplcDigitalOutputsRelay0, ...rpiplcDigitalOutputsRelay1, ...rpiplcDigitalOutputsRelay2],
		"RPIPLC_58": [...rpiplcV3ExtraDigitalOutputs, ...rpiplcDigitalOutputsAnalog0, ...rpiplcDigitalOutputsAnalog1, ...rpiplcDigitalOutputsAnalog2]
	};

	const pinsMapDigitalOutputPins = {
		"RPIPLC_V6": rpiplcV6DigitalOutputPins,
		"RPIPLC_V4": rpiplcV4DigitalOutputPins,
		"RPIPLC_V3": rpiplcV3DigitalOutputPins,
		"UPSAFEPI_V6": {"UPSAFEPI": ["DE_RE"]},
		"GATEBERRY_V9": {"GATEBERRY": ["DE_RE", "EXP_RST", "EXP_CS", "EXP_PWM"]},
		"TOUCHBERRY_PI_V1": {"TOUCHBERRY_PI": ["Q0", "Q1", "Q2", "Q3", "Q4", "RE", "DE", "CS0", "CS1", "EXP1_RST", "O0", "O1", "O2", "O3", "O4"]},
	};

	RED.nodes.registerType("rpiplc-digital-write", {
		category: "Industrial Shields",
		color: "#C7E9C0",

		defaults: {
			rpiplc: { value: "", type: "rpiplc-config", required: true },
			pin: { value: "", required: true },
			value: { value: null },
			name: { value: "" },
		},

		inputs: 1,
		outputs: 1,

		icon: "serial.svg",
		align: "left",
		labelStyle: "node_label",
		label: function() {
			if (this.name) {
				return this.name;
			}
			if (this.pin) {
				let pinStr = this.pin !== "CUSTOM" ? this.pin : "custom"
				if (this.value === '') {
					return `write ${pinStr}`;
				}
				return `write ${pinStr}: ${this.value}`;
			}
			return "invalid digital write";
		},
		paletteLabel: function() {
			return this.name || "digital write";
		},

		oneditprepare: function() {
			const digitalWriteNode = this;

			$("#node-input-rpiplc").change(function() {
				const hideInputDiv = $("#hide-node-input-pin");
				const hideValueDiv = $("#hide-node-value-pin");
				const pinSelect = $("#node-input-pin");

				function hidePins() {
					hideInputDiv.show();
					hideInputDiv.hide();
					pinSelect.empty();
					pinSelect.append($("<option></option>").val(null).text("You found a bug!"));
				}

				const configId = $(this).val();
				if (!configId) {
					hidePins();
					return;
				}
				const configNode = RED.nodes.node(configId);
				if (!configNode) {
					hidePins();
					return;
				}

				const version = configNode.version;
				const model = configNode.model;
				if (version === "CUSTOM") {
					hidePins();
					pinSelect.append($("<option></option>").val("CUSTOM").text("CUSTOM"));
					pinSelect.val("CUSTOM");
					return;
				}

				const pinsArray = pinsMapDigitalOutputPins[version][model];
				if (pinsArray.length !== 0) {
					const actualValue = pinSelect.val() || digitalWriteNode.pin;
					pinSelect.empty();
					pinsArray.forEach((pin) => {
						pinSelect.append($("<option></option>").val(pin).text(pin))
					});
					pinSelect.append($("<option></option>")
						.val("Message passed").text("Message passed"))
					// Replace if some pin was already set in the node
					if (pinsArray.includes(actualValue) || actualValue === "Message passed") {
						pinSelect.val(actualValue);
					}
					// Clean the error border if present
					if (pinSelect.hasClass("input-error")) {
						pinSelect.removeClass("input-error");
					}
					hideInputDiv.show();
					hideValueDiv.show();
				}
				else {
					hidePins();
				}
			});
		},
	});
</script>

<script type="text/x-red" data-template-name="rpiplc-digital-write">
	<div class="form-row">
		<label for="node-input-rpiplc"><i class="fa fa-cog"></i> PLC type</label>
		<input type="text" id="node-input-rpiplc" placeholder="PLC type">
	</div>
	<div class="form-row" id="hide-node-input-pin">
		<label for="node-input-pin"><i class="fa fa-circle-o"></i> Output</label>
		<select id="node-input-pin" placeholder="Select an output"></select>
	</div>
	<div class="form-row" id="hide-node-input-value">
		<label for="node-input-value"><i class="fa fa-sliders"></i> Value</label>
		<select id="node-input-value" placeholder="Value">
			<option value="0">LOW</option>
			<option value="1">HIGH</option>
			<option value="">Message passed</option>
		</select>
	</div>
	<div class="form-row">
		<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
		<input type="text" id="node-input-name" placeholder="Name">
	</div>
</script>

<script type="text/x-red" data-help-name="rpiplc-digital-write">
  <p>
    <b>Digital Write</b>
  </p>
  <p>
    Writes a digital value in the specified pin. It returns a <code>msg.rc</code> attribute to
    check if the digital write was successful.
  </p>
  <p>
    If the selected pin is <code>Message passed</code> or if the configuration node is custom, this
    node will read the <code>msg.pin</code> attribute to determine which pin to read from.
  </p>

  <p>The Value field can take as possible values:</p>
  <ul>
    <li><code>HIGH</code></li>
    <li><code>LOW</code></li>
    <li><strong>Message passed</strong></li>
  </ul>
  <p>
  If <strong>Message passed</strong>, the payload can take the following values:
  <ul>
    <li><code>"HIGH"</code> or <code>"LOW"</code> strings.</li>
    <li><code>"1"</code> or <code>"0"</code> strings.</li>
    <li>A boolean (either <code>true</code> or <code>false</code>).</li>
    <li>A number (either <code>1</code> or <code>0</code>).</li>
  </ul>
  <p>
    See also the <a href="https://www.industrialshields.com/blog/raspberry-pi-for-industry-26/post/node-red-tutorial-how-to-set-digital-outputs-to-raspberry-plc-334#scrollTop=0">online documentation</a> for more information.
  </p>

</script>
